# -*- coding: UTF-8 -*-
# @Time : 2021/12/23 0023 13:41
# @Author : lyw
# @Remark : 普通空调控制Server
import json
import logging

import socket
import traceback
from typing import List

from modbus_tk.exceptions import ModbusInvalidResponseError

from db.common.IOTManager.AirConditioner.modbus_ac import ACSelectDB, ACUpdateDB, ACSetDB, ACRetrieveDB
try:
    from opm_pyirm import GetStatusById
except ImportError:
    try:
        from bsmiddle import GetStatusById
    except ImportError:
        def GetStatusById(*args):
            return 1, 2

from model.common.IOTManager.iot_base import SRoomIOTMiddleModel
from server.public.IOTManager.server_base import IOTBaseServer
from utils.classes.custom_exception import VerifyError
from utils.public.modbus_method import ConnentModbus
from index import _


class ACSelectServer(IOTBaseServer):
    __db__ = ACSelectDB
    __model__ = SRoomIOTMiddleModel
    search = "ac_name"
    m_filed_run = "ac_run"
    m_run = "run_state"
    s_state = "ac_state"

    def device_info(self, device_list, state_dict: dict = None):
        """
        获取设备信息
        @param state_dict:
        @param device_list:
        @return:
        """
        res_device_data = []
        if state_dict is None:
            state_dict = {"state": 1}
        if state_dict["state"] != 2:
            state_dict["state"] = 1
        for device_data in device_list:
            status = GetStatusById(device_data["code"])
            if isinstance(status ,int):
                status = status
            else:
                status = status[1]
            if state_dict is not None:
                if status != 1:
                    state_dict["state"] = 2
            info_data = {"name": device_data["mxlabel"], "code": device_data["code"],
                         "state": self.set_state(status),
                         "local": self.db.get_local(
                             self.model(device_type=self.db.device_type, iot_code=device_data["code"],
                                        sroom_code="")),
                         "alarm_info": "", "auto_option": "",
                         "running_model": "", "power_on": "", "indoor_temp": "", "indoor_humidity": "",
                         "run_state": "", "mxdevicetype": device_data["mxdevicetype"],
                         }
            # 获取告警信息
            self.alert_info(device_data, info_data)

            # 获取  数据库数据
            self.query_table_data(info_data, device_data)
            try:
                self.query_modbus_data(info_data, device_data)
            except (
                    socket.gaierror, ConnectionResetError, ConnectionRefusedError, socket.timeout,
                    ModbusInvalidResponseError
            ) as e:
                logging.error(e)
            res_device_data.append(info_data)

        return res_device_data

    def query_table_data(self, info_data, device_data):
        """
        获取温度湿度信息
        @return:
        """
        record_dict = self.db.query_table(device_data["code"])
        info_data["indoor_temp"] = "{temp}℃".format(temp=record_dict.get("indoor_temp", ""))
        info_data["indoor_humidity"] = "{humidity}%".format(humidity=record_dict.get("indoor_humidity", ""))

    @staticmethod
    def query_modbus_data(info_data, device_data):
        """
        获取modbus信息
        @param info_data:
        @param device_data:
        @return:
        """
        cm = ConnentModbus(host=device_data["_target_ip"], port=int(device_data["_port"]),
                           salve=int(device_data["_Addr"]))
        # 读取自动控制的数据
        auto_option = cm.read_data(function_code=3, start_addr=115, quantity=1)[0]
        # 读取运行模式数据
        running_model = cm.read_data(function_code=3, start_addr=215, quantity=1)[0]
        # 读取来电自启的数据
        power_on = cm.read_data(function_code=3, start_addr=67, quantity=1)[0]
        info_data["auto_option"] = "开启" if auto_option else "关闭"
        info_data["running_model"] = ("制冷" if running_model == 1 else "制热") if running_model in (1, 2) else "停止"
        info_data["power_on"] = "开启" if power_on else "关闭"
        info_data["run_state"] = "开" if running_model in (1, 2) else "关"


class ACUpdateServer(IOTBaseServer):
    __db__ = ACUpdateDB

    def update(self, **kwargs):
        """
        更新状态
        @param kwargs:
        @return:
        """
        ac_data = self.db.retrieve(kwargs["ac_code"])

        ac_option = {"auto": {"off": {"code": 115, "data": 0}, "on": {"code": 115, "data": 1}},
                     "run_state": {"off": {"code": 187, "data": 1}, "on": {"code": 185, "data": 1}},
                     "power_on": {"off": {"code": 67, "data": 0}, "on": {"code": 67, "data": 1}},
                     "run_model": {"col": {"code": 185, "data": 1}, "hea": {"code": 186, "data": 1},
                                   "stop": {"code": 187, "data": 1}}}  # 定义操作选项

        if not ac_option.get(kwargs["ac_option"]):
            raise VerifyError(_("ac_option 参数错误"))

        if not ac_option[kwargs["ac_option"]].get(kwargs["option"]):
            raise VerifyError(_("option 参数错误"))

        option_data = ac_option[kwargs["ac_option"]][kwargs["option"]]
        try:
            cm = ConnentModbus(host=ac_data["_target_ip"], port=int(ac_data["_port"]), salve=int(ac_data["_Addr"]))
            cm.write_data(function_code=6, addr=option_data["code"], data=option_data["data"])
        except (socket.gaierror, ConnectionResetError, ConnectionRefusedError, socket.timeout) as e:
            logging.error(e)
        return True


class ACSetServer(IOTBaseServer):
    __db__ = ACSetDB

    def __init__(self):
        super().__init__()
        self.cm = None

    def update(self, **kwargs):
        """
        设置限制和时间
        @param kwargs:
        @return:
        """
        ac_data = self.db.retrieve(kwargs["ac_code"])
        self.cm = ConnentModbus(host=ac_data["_target_ip"], port=int(ac_data["_port"]), salve=int(ac_data["_Addr"]))
        # 更新限制参数
        self.update_limit_data(**kwargs)
        # 更新运行时间
        # self.update_running_data(kwargs["running_list"])
        self.update_running_data(json.loads(kwargs["running_list"]))
        return True

    def update_limit_data(self, **kwargs):
        """
        更新限制参数
        @param kwargs:
        @return:
        """
        limit_param = {"upper_limit": 108, "lower_limit": 109, "hysteresis": 110}

        try:
            for limit_key, limit_value in limit_param.items():
                self.cm.write_data(function_code=6, addr=limit_value, data=int(kwargs[limit_key]) * 10)
        except (socket.gaierror, ConnectionResetError, ConnectionRefusedError, socket.timeout) as e:
            logging.error(e)
            raise VerifyError(_("设置失败"))

    def update_running_data(self, running_list: List[dict]):
        """
        更新运行时间
        @param running_list:
        @return:
        """
        running_list = {i["index"]: i for i in running_list}
        it = iter(range(68, 68 + 16))

        def get_time_quantum_list(index):
            """
            获取时间列表
            """
            try:
                time_quantum_list = list()
                time_quantum_list.extend(running_list[index + 1]["start"].split(":"))
                time_quantum_list.extend(running_list[index + 1]["end"].split(":"))
                if len(time_quantum_list) != 4:
                    return ["00", "00", "00", "00"]
            except KeyError:
                return ["00", "00", "00", "00"]

            return time_quantum_list

        try:
            for range_index in range(4):
                for time_quantum in get_time_quantum_list(range_index):
                    self.cm.write_data(function_code=6, addr=next(it), data=int(time_quantum))

        except (socket.gaierror, ConnectionResetError, ConnectionRefusedError, socket.timeout) as e:
            logging.error(e)
            raise VerifyError(_("设置失败"))
        except KeyError:
            logging.error(traceback.format_exc())
            raise VerifyError(_("请检查参数 running_list"))


class ACRetrieveServer(IOTBaseServer):
    __db__ = ACRetrieveDB

    def __init__(self):
        super().__init__()
        self.cm = None

    def retrieve(self, **kwargs):
        ac_data = self.db.retrieve(kwargs["ac_code"])
        running_list = []
        retrieve_data = {"upper_limit": 0, "lower_limit": 0, "hysteresis": 0, "running_list": running_list}

        self.cm = ConnentModbus(host=ac_data["_target_ip"], port=int(ac_data["_port"]), salve=int(ac_data["_Addr"]))
        # 获取 上限信息
        self.query_limit_data(retrieve_data)
        # 获取 运行时间
        self.query_running_data(running_list.append)
        return retrieve_data

    def query_limit_data(self, res_data):
        """
        获取限制信息
        @param res_data:
        @return:
        """
        try:
            limit_data = self.cm.read_data(function_code=3, start_addr=108, quantity=3)
            res_data["upper_limit"] = limit_data[0] / 10
            res_data["lower_limit"] = limit_data[1] / 10
            res_data["hysteresis"] = limit_data[2] / 10
        except (socket.gaierror, ConnectionResetError, ConnectionRefusedError, socket.timeout) as e:
            logging.error(e)
            raise VerifyError(_("获取失败"))

    def query_running_data(self, running_list: list.append):
        try:

            run_tuple = self.cm.read_data(function_code=3, start_addr=68, quantity=16)
            # running_list(run_tuple)
            for range_index in range(4):
                time_quantum = run_tuple[4 * range_index:4 * range_index + 4]
                # 判断是否都是0
                if self.verify_time_quantum(time_quantum):
                    continue
                running_list({"index": range_index + 1,
                              "start": ":".join(
                                  (str(time_quantum[0]).rjust(2, "0"), str(time_quantum[1]).rjust(2, "0"))),
                              "end": ":".join((str(time_quantum[2]).rjust(2, "0"), str(time_quantum[3]).rjust(2, "0"))),
                              })
        except (socket.gaierror, ConnectionResetError, ConnectionRefusedError, socket.timeout) as e:
            logging.error(e)
            raise VerifyError(_("获取失败"))

    @staticmethod
    def verify_time_quantum(time_quantum):
        """
        验证时间是不是已设定啦
        @param time_quantum:
        @return:
        """
        result = False
        count = 0
        for time_data in time_quantum:
            if time_data == 0:
                count += 1
        if len(time_quantum) == count:
            result = True
        return result


class ACHintServer(ACRetrieveServer):
    __db__ = ACRetrieveDB

    def __init__(self):
        super().__init__()
        self.cm = None

    def retrieve(self, **kwargs):
        ac_data = self.db.retrieve(kwargs["ac_code"])
        retrieve_data = {"upper_limit": 0, "lower_limit": 0, "hysteresis": 0}

        self.cm = ConnentModbus(host=ac_data["_target_ip"], port=int(ac_data["_port"]), salve=int(ac_data["_Addr"]))
        # 获取 上限信息
        self.query_limit_data(retrieve_data)
        res_bool = False
        for k, v in retrieve_data.items():
            if v == 0:
                res_bool = True
        # 结果判断是否提示设置上下限
        return res_bool
